<!doctype html>



  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Mybatis," />








  <link rel="shortcut icon" type="image/x-icon" href="/blog/favicon.ico?v=5.1.0" />






<meta name="description" content="前段时间新项目考虑到日后数据量的问题，采用了分表分库的方式来存储数据。我们数据库用的是mysql。ORM框架采用的是Mybatis。而分表分库为公司内部开发的Mybatis插件。在项目开发完成之后，对这个分表分库的插件很感兴趣。也看看公司内部的分表分库插件是如何实现的。 Mybatis插件其实分表分库的插件通过Mybatis拦截器实现的插件。通过Mybatis拦截器功能能够轻松实现很多其他插件；例">
<meta name="keywords" content="Mybatis">
<meta property="og:type" content="article">
<meta property="og:title" content="MyBatis自定义插件">
<meta property="og:url" content="https://gitee.com/shuhao/2018/08/29/MyBatis自定义插件/index.html">
<meta property="og:site_name" content="Niki">
<meta property="og:description" content="前段时间新项目考虑到日后数据量的问题，采用了分表分库的方式来存储数据。我们数据库用的是mysql。ORM框架采用的是Mybatis。而分表分库为公司内部开发的Mybatis插件。在项目开发完成之后，对这个分表分库的插件很感兴趣。也看看公司内部的分表分库插件是如何实现的。 Mybatis插件其实分表分库的插件通过Mybatis拦截器实现的插件。通过Mybatis拦截器功能能够轻松实现很多其他插件；例">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2018-08-30T09:34:33.388Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="MyBatis自定义插件">
<meta name="twitter:description" content="前段时间新项目考虑到日后数据量的问题，采用了分表分库的方式来存储数据。我们数据库用的是mysql。ORM框架采用的是Mybatis。而分表分库为公司内部开发的Mybatis插件。在项目开发完成之后，对这个分表分库的插件很感兴趣。也看看公司内部的分表分库插件是如何实现的。 Mybatis插件其实分表分库的插件通过Mybatis拦截器实现的插件。通过Mybatis拦截器功能能够轻松实现很多其他插件；例">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Mist',
    sidebar: {"position":"right","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://gitee.com/shuhao/2018/08/29/MyBatis自定义插件/"/>





  <title> MyBatis自定义插件 | Niki </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?cb9f0585e406f878cb2f29c89ad3cdb7";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Niki</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">天道酬勤</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-categories"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/shuhao/blog/2018/08/29/MyBatis自定义插件/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Fang ShuHao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/head.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Niki">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                MyBatis自定义插件
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-08-29T10:42:36+08:00">
                2018-08-29
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Mybatis/" itemprop="url" rel="index">
                    <span itemprop="name">Mybatis</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>前段时间新项目考虑到日后数据量的问题，采用了分表分库的方式来存储数据。我们数据库用的是mysql。ORM框架采用的是Mybatis。而分表分库为公司内部开发的Mybatis插件。<br>在项目开发完成之后，对这个分表分库的插件很感兴趣。也看看公司内部的分表分库插件是如何实现的。</p>
<h2 id="Mybatis插件"><a href="#Mybatis插件" class="headerlink" title="Mybatis插件"></a>Mybatis插件</h2><p>其实分表分库的插件通过Mybatis拦截器实现的插件。通过Mybatis拦截器功能能够轻松实现很多其他插件；例如：Mybatis的分页插件。<br>我们一起来看下MyBatista的插件是如何实现这些功能的。</p>
<p>首先我们需要明白的问题有：</p>
<ol>
<li>如何新建插件</li>
<li>插件如何配置</li>
<li>插件如何工作</li>
</ol>
<h3 id="新建插件"><a href="#新建插件" class="headerlink" title="新建插件"></a>新建插件</h3><p>Mybatis新建插件都必须实现接口：Interceptor。（没错就是拦截器）<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Interceptor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function">Object <span class="title">intercept</span><span class="params">(Invocation invocation)</span> <span class="keyword">throws</span> Throwable</span>;</span><br><span class="line"></span><br><span class="line">  <span class="function">Object <span class="title">plugin</span><span class="params">(Object target)</span></span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">setProperties</span><span class="params">(Properties properties)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这个接口共三个方法</p>
<ol>
<li>intercept： 拦截器具体逻辑实现。</li>
<li>plugin：生成目标类的代理。代理逻辑由intercept方法提供。</li>
<li>setProperties：为该代理对象设置额外的参数<br>我们来看下plugin方法的实现。<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">plugin</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> Plugin.wrap(target, <span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">wrap</span><span class="params">(Object target, Interceptor interceptor)</span> </span>&#123;</span><br><span class="line">    Map&lt;Class&lt;?&gt;, Set&lt;Method&gt;&gt; signatureMap = getSignatureMap(interceptor);</span><br><span class="line">    Class&lt;?&gt; type = target.getClass();</span><br><span class="line">    Class&lt;?&gt;[] interfaces = getAllInterfaces(type, signatureMap);</span><br><span class="line">    <span class="keyword">if</span> (interfaces.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> Proxy.newProxyInstance(</span><br><span class="line">          type.getClassLoader(),</span><br><span class="line">          interfaces,</span><br><span class="line">          <span class="keyword">new</span> Plugin(target, interceptor, signatureMap));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> target;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个方法我们能很明显的看到Java代理模式的痕迹<strong>Proxy.newProxyInstance</strong>。Mybatis会为每个target对象生成一个Plugin类型的代理对象。如果存在多个插件时，Mybatis采用的是<strong>责任链</strong>的模式。例如target对象被插件A拦截生成代理对象PluginProxyA，随后，又被插件B拦截则代理对象不再是target，而是<strong>PluginProxyA</strong>，生成代理对象PluginProxyB。<br>熟悉Java代理模式的同学都知道。在代理类Proxy中通过invoke方法来增强Target类能力。<br>我们看下Plugin这个Proxy的invoke方法<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      Set&lt;Method&gt; methods = signatureMap.get(method.getDeclaringClass());</span><br><span class="line">      <span class="keyword">if</span> (methods != <span class="keyword">null</span> &amp;&amp; methods.contains(method)) &#123;</span><br><span class="line">      	<span class="comment">// interceptor是创建代理Plugin时传入的插件对象</span></span><br><span class="line">        <span class="keyword">return</span> interceptor.intercept(<span class="keyword">new</span> Invocation(target, method, args));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> method.invoke(target, args);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">      <span class="keyword">throw</span> ExceptionUtil.unwrapThrowable(e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>所以，拦截器最终调用到Interception的intercept(Invocation invocation) 方法中。所以我们插件需要实现的逻辑也应该在该方法中实现。</p>
<p>最后放一个Mybatis插件demo<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Intercepts</span>(&#123;<span class="meta">@org</span>.apache.ibatis.plugin.Signature(type = StatementHandler.class, method = <span class="string">"prepare"</span>, args = &#123;java.sql.Connection.class&#125;)&#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShardPlugin</span> <span class="keyword">implements</span> <span class="title">Interceptor</span> </span>&#123;</span><br><span class="line">	<span class="keyword">private</span> Properties pluginProperties ;</span><br><span class="line">	<span class="keyword">private</span> Map&lt;String, String&gt; table2DB = Maps.newHashMap();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分库分表规则解析策略</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, ShardStrategy&gt; strategies = Maps.newHashMap();</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 实现分表分库逻辑</span></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Invocation invocation)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">    	<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (CollectionUtils.isEmpty(table2DB))</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"请初始化逻辑表 与数据库映射"</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            BoundSql boundSql = getBoundSql(invocation);</span><br><span class="line"></span><br><span class="line">            LOGGER.debug(<span class="string">"Shard Original SQL:&#123;&#125;"</span>, boundSql.getSql());</span><br><span class="line"></span><br><span class="line">            SqlParser sqlParser = SqlParserFactory.getInstance().createParser(boundSql.getSql());</span><br><span class="line"></span><br><span class="line">            List&lt;Table&gt; tables = sqlParser.getTables();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (tables.isEmpty())</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">return</span> invocation.proceed();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            doSharding(tables, boundSql, sqlParser);</span><br><span class="line">            <span class="keyword">return</span> invocation.proceed();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            ShardHint.clear();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 为目标类添加分表分库代理</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">plugin</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Plugin.wrap(target, <span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 为分表分库插件设置额外参数</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setProperties</span><span class="params">(Properties properties)</span> </span>&#123;</span><br><span class="line">    	<span class="keyword">this</span>.pluginProperties = properties;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>可能有小伙伴注意到这个插件实现类还有一段注解：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@Intercepts(&#123;@org.apache.ibatis.plugin.Signature(type = StatementHandler.class, method = &quot;prepare&quot;, args = &#123;java.sql.Connection.class&#125;)&#125;)</span><br></pre></td></tr></table></figure></p>
<p>该注解用来设置插件拦截的目标类。也就是plugin方法中target的类型。</p>
<p>Mybatis允许拦截的类有<strong>四种</strong></p>
<ol>
<li>Executor</li>
<li>StatementHandler</li>
<li>PameterHandler</li>
<li>ResultSetHandler</li>
</ol>
<h3 id="Mybatis-设置插件"><a href="#Mybatis-设置插件" class="headerlink" title="Mybatis 设置插件"></a>Mybatis 设置插件</h3><p>我们实现好了插件，如何把它配置到Mybatis上使之生效呢？这里我们仅提供API实现的方式。<br>在我们配置SqlSessionFactory时进行配置<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> SqlSessionFactory <span class="title">sqlSessionFactory</span><span class="params">()</span></span>&#123;</span><br><span class="line">	SqlSessionFactoryBean factoryBean = <span class="keyword">new</span> SqlSessionFactoryBean();</span><br><span class="line">	<span class="comment">// ... factoryBean其他配置</span></span><br><span class="line">	List&lt;Interceptor&gt; interceptors = <span class="keyword">new</span> ArrayList&lt;Interceptor&gt;();</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 分表分库插件</span></span><br><span class="line">	ShardPlugin shardPlugin = <span class="keyword">new</span> ShardPlugin();</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 为分页插件设置额外参数</span></span><br><span class="line">	Properties shardProperties = <span class="keyword">new</span> Properties();</span><br><span class="line">    shardProperties.setProperty(<span class="string">"configsLocation"</span>, <span class="string">"sharding/mybatis-sharding-config.xml"</span>);</span><br><span class="line">    shardPlugin.setProperties(shardProperties);</span><br><span class="line">    interceptors.add(shardPlugin);</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 向Mybatis注册分表分库插件</span></span><br><span class="line">    factoryBean.setPlugins(interceptors.toArray(<span class="keyword">new</span> Interceptor[interceptors.size()]));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> factoryBean.getObject();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="插件如何工作，拦截"><a href="#插件如何工作，拦截" class="headerlink" title="插件如何工作，拦截"></a>插件如何工作，拦截</h3><p>我们首先来看Mybatis把我们的插件对象都放到了哪里。我们尽到factoryBean.getObject()方法中，发现，SQLSessionFactory对象的创建被委托给方法buildSqlSessionFactory()。在该方法内有这段代码<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="keyword">this</span>.plugins)) &#123;</span><br><span class="line">    Interceptor[] arr$ = <span class="keyword">this</span>.plugins;</span><br><span class="line">    len$ = arr$.length;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i$ = <span class="number">0</span>; i$ &lt; len$; ++i$) &#123;</span><br><span class="line">        Interceptor plugin = arr$[i$];</span><br><span class="line">        <span class="comment">// 插件对象都交给了Configuration对象</span></span><br><span class="line">        configuration.addInterceptor(plugin);</span><br><span class="line">        <span class="keyword">if</span> (LOGGER.isDebugEnabled()) &#123;</span><br><span class="line">            LOGGER.debug(<span class="string">"Registered plugin: '"</span> + plugin + <span class="string">"'"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>进到Configuration对象<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> InterceptorChain interceptorChain = <span class="keyword">new</span> InterceptorChain();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addInterceptor</span><span class="params">(Interceptor interceptor)</span> </span>&#123;</span><br><span class="line">interceptorChain.addInterceptor(interceptor);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>所有的插件又被放到InterceptorChain内部的一个List中（当初List转Array，这有转回了List^~^）</p>
<p>我们已经知道我们的插件Interceptor的去处，那么Mybatis在哪里调用他们？<br>还是在Configuration类中有如下四个方法<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ParameterHandler <span class="title">newParameterHandler</span><span class="params">(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql)</span> </span>&#123;</span><br><span class="line">  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);</span><br><span class="line">  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);</span><br><span class="line">  <span class="keyword">return</span> parameterHandler;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> ResultSetHandler <span class="title">newResultSetHandler</span><span class="params">(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,</span></span></span><br><span class="line"><span class="function"><span class="params">    ResultHandler resultHandler, BoundSql boundSql)</span> </span>&#123;</span><br><span class="line">  ResultSetHandler resultSetHandler = <span class="keyword">new</span> DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);</span><br><span class="line">  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);</span><br><span class="line">  <span class="keyword">return</span> resultSetHandler;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> StatementHandler <span class="title">newStatementHandler</span><span class="params">(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)</span> </span>&#123;</span><br><span class="line">  StatementHandler statementHandler = <span class="keyword">new</span> RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);</span><br><span class="line">  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);</span><br><span class="line">  <span class="keyword">return</span> statementHandler;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Executor <span class="title">newExecutor</span><span class="params">(Transaction transaction)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> newExecutor(transaction, defaultExecutorType);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我们看到创建这四种对象的方法都调用了InterceptorChain的pluginAll方法<br><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">pluginAll</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (Interceptor interceptor : interceptors) &#123;</span><br><span class="line">      target = interceptor.plugin(target);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> target;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这里很明显通过自定义的Interceptor插件生成代理对象。这也和上面的内容相吻合。</p>
<p>好了。这就是我总结的Mybatis插件实现过程。在其中，我们看到其实现原理主要通过<strong>代理模式</strong>、和<strong>责任链模式</strong>实现对CRUD的增强或修改。<br>主要涉及到的类有：</p>
<ul>
<li>Interceptor</li>
<li>Plugin</li>
<li>InterceptorChain</li>
<li>Configuration<br>另外需要注意的是：</li>
</ul>
<ol>
<li>Mybatis会为每个插件生成一个代理，有N个插件就有N个代理，每个代理都要执行删除逻辑。比较影响性能，除非必要，不随便编写插件。</li>
<li>在Interceptor实现类中，实现plugin方法时，判断一下目标对象，如非目标对象，直接返回目标对象，减少目标被代理的次数</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">plugin</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> target <span class="keyword">instanceof</span> StatementHandler ? Plugin.wrap(target, <span class="keyword">this</span>) : target ;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/Mybatis/" rel="tag"># Mybatis</a>
          
        </div>
      

      
        
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2018/08/21/Maven之Dependencies与DependencyManagement/" rel="next" title="Maven之Dependencies与DependencyManagement">
                <i class="fa fa-chevron-left"></i> Maven之Dependencies与DependencyManagement
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2018/08/30/logback源码阅读/" rel="prev" title="logback源码阅读">
                logback源码阅读 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>

          
          </div>
          


          
  <div class="comments" id="comments">
    
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zNjY5NS8xMzIzMA=="></div>
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/blog/images/head.jpg"
               alt="Fang ShuHao" />
          <p class="site-author-name" itemprop="name">Fang ShuHao</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">
        
          
            <div class="site-state-item site-state-posts">
              <a href="/blog/archives">
                <span class="site-state-item-count">135</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-categories">
              <a href="/blog/categories">
                <span class="site-state-item-count">35</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/blog/tags">
                <span class="site-state-item-count">93</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Mybatis插件"><span class="nav-number">1.</span> <span class="nav-text">Mybatis插件</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#新建插件"><span class="nav-number">1.1.</span> <span class="nav-text">新建插件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Mybatis-设置插件"><span class="nav-number">1.2.</span> <span class="nav-text">Mybatis 设置插件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#插件如何工作，拦截"><span class="nav-number">1.3.</span> <span class="nav-text">插件如何工作，拦截</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Fang ShuHao</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Mist
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    
    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  




  
  <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.0"></script>



  
  

  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.0"></script>



  



  




	





  





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  



  
  

  

  

  

  


  

</body>
</html>
